linux gadget 驱动
介绍USB系统框架,只关注框架部分,不涉及细节
这里的USB设备控制器(UDC)驱动指作为其他usb主机控制器外设的usb硬件设备上底层硬件控制器的驱动,该硬件和驱动负责将一个usb设备依附于一个usb主机控制器上。
在usb设备控制器于gadget驱动中,我们主要关心几个核心的数据结构。描述一个usb设备控制器的usb_gadget,描述一个gadget驱动的usb_gadget_driver,表示一个传输请求的usb_request,描述一个端点的usb_ep,描述端点操作的usb_ep_ops结构体
研究时使用9x07平台
初始化流程
## 添加udc设备
以ci3xxx_msm举例
初始化gadget
ci13xxx_msm_probe->udc_probe
1
2
3
4
5
6
7
8
9
10
11
12udc = kzalloc(sizeof(struct ci13xxx), GFP_KERNEL);
if (udc == NULL)
return -ENOMEM;
udc->lock = &udc_lock;
udc->regs = regs;
udc->udc_driver = driver;
udc->gadget.ops = &usb_gadget_ops;
udc->gadget.speed = USB_SPEED_UNKNOWN;
udc->gadget.max_speed = USB_SPEED_HIGH;
udc->gadget.is_otg = 0;
udc->gadget.name = driver->name;
udc->gadget.usb_core_id没有初始化,默认值为0
创建&添加udc设备
retval = usb_add_gadget_udc(dev, &udc->gadget);
usb_add_gadget_udc_release(parent, gadget, NULL)
1 | udc = kzalloc(sizeof(*udc), GFP_KERNEL) |
添加android设备
- 从android_probe开始
从设备树中获取usb_core_id,默认值为0,创建出来的设备是android0
android_dev = kzalloc(sizeof(*android_dev), GFP_KERNEL);
android_create_device(struct android_dev *dev, u8 usb_core_id)
1 | snprintf(device_node_name, ANDROID_DEVICE_NODE_NAME_LENGTH, |
创建一个android设备,其中usb_core_id默认为0
初始化设备(android0)默认支持的功能(function)
1
2
3
4android_dev->name = pdev->name;
android_dev->disable_depth = 1;
android_dev->functions =
supported_list ? supported_list : default_functions;
其中supported_list从设备树获取,默认为空,既使用default_functions
绑定udc设备
usb_composite_probe(&android_usb_driver)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18int usb_composite_probe(struct usb_composite_driver *driver)
{
struct usb_gadget_driver *gadget_driver;
u8 core_id;
//初始化android_usb_driver下的gadget_driver
core_id = driver->gadget_driver.usb_core_id;
driver->gadget_driver = composite_driver_template;
gadget_driver = &driver->gadget_driver;
gadget_driver->function = (char *) driver->name;
gadget_driver->driver.name = driver->name;
gadget_driver->max_speed = driver->max_speed;
if (core_id)
gadget_driver->usb_core_id = core_id;
return usb_gadget_probe_driver(gadget_driver);
}usb_gadget_probe_driver(struct usb_gadget_driver *driver)
根据usb_core_id找到udc(在ci13xxx_msm_probe中添加的) 将udc和driver绑定:udc_bind_to_driver
udc_bind_to_driver
ret = driver->bind(udc->gadget, driver);
执行composite_bindcomposite_bind
创建 usb_composite_dev设备1
2cdev = kzalloc(sizeof *cdev, GFP_KERNEL)
cdev->gadget = gadget;执行usb_composite_driver的bind,此处是android_bind
android_bind
初始化产品信息(可以通过应用层修改,修改/sys/class/android_usb/android0目录下的文件)
初始化&创建function(可以通过应用层修改,修改/sys/class/android_usb/android0/functions)
应用层修改&使能
参数&配置修改:修改修改/sys/class/android_usb/android0下的文件,暂不关注系统
使能
应用层修改/sys/class/android_usb/android0/enable文件触发android_enable
添加配置
usb_add_config(cdev, &conf->usb_config, android_bind_config);
android_bind_config —> android_bind_enabled_functions
遍历配置里的所有function,执行相应的bind_config连接,触发host端的连接请求
usb_gadget_connect(cdev->gadget);gadget->ops->pullup(gadget, 1)
gadget配置
host端请求代码:USB_REQ_GET_DESCRIPTOR –> USB_DT_DEVICE
1 | int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) |
device端请求:入口:composite_setup
1 | switch (ctrl->bRequest) { |
1 | static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) |
接口数量
c->bNumInterfaces = config->next_interface_id
next_interface_id在usb_interface_id中修改,每调用一次usb_interface_id,next_interface_id加1
usb_interface_id在各个function的bind_config函数中调用
f_rndis的interface数量为2,包含控制接口和数据接口
1 | /* allocate instance-specific interface IDs */ |
f_rmnet的interface数量可变,具体如下:
- 使能android接口之前需要设置rmnet_transports(f_rmnet/transports文件)
- rmnet_function_bind_config 遍历rmnet_transports配置,执行frmnet_init_port
- frmnet_init_port初始化rmnet_port,记录port总数量
- bind:每个端口执行一次frmnet_bind_config , frmnet_bind —> usb_interface_id,为每个端口添加一个interface
rmnet用为拨号接口有更复杂的其它功能,不在此描述
f_diag的interface数量可变,具体如下:
- 使能android接口之前需要设置diag_clients(f_diag/clients文件)
- diag_function_bind_config遍历diag_clients配置,执行diag_function_add
- bind:diag_function_bind —> usb_interface_id,为每个配置添加一个interface
f_serail的interface数量可变,具体如下:
使能android接口之前需要设置serial_transports(f_serial/transports文件)
serial_function_bind_config遍历serial_transports配置,执行gserial_init_port
gserial_init_port初始化gserial_ports,记录port总数量
bind: 每个端口执行一次gser_alloc,gser_bind —> usb_interface_id,为每个端口添加一个interface
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23for (i = 0; i < ports; i++) {
config->f_serial_inst[i] = usb_get_function_instance("gser");
if (IS_ERR(config->f_serial_inst[i])) {
err = PTR_ERR(config->f_serial_inst[i]);
goto err_gser_usb_get_function_instance;
}
config->f_serial[i] = usb_get_function(config->f_serial_inst[i]);
if (IS_ERR(config->f_serial[i])) {
err = PTR_ERR(config->f_serial[i]);
goto err_gser_usb_get_function;
}
}
serial_initialized = 1;
bind_config:
for (i = 0; i < ports; i++) {
err = usb_add_function(c, config->f_serial[i]);
if (err) {
pr_err("Could not bind gser%u config\n", i);
goto err_gser_usb_add_function;
}
}
接口编号
host端的驱动根据device端的接口编号来匹配
接口编号按照注册顺序生成(遍历functions),比如:
1 | echo diag > f_diag/clients |
编号0:diag
编号1: tty
编号2:smd
编号3:smd
编号4:rmnet
endpoint
从usb 主机到设备称为 out 端点,从设备到主机称为in 端点。
创建endpoint
udc初始化时会创建endpoint
1 | for (i = 0; i < hw_ep_max/2; i++) { |
创建的endpoint由adget.ep_list管理
1 | static const struct usb_ep_ops usb_ep_ops = { |
重点关注usb_ep_ops
申请endpoint
每个接口(interface)bind时会申请endpoint,比如:
1 | ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc); |
数据通讯
host端的控制请求响应
udc_irq —> isr_tr_complete_handler —> udc->driver->setup
composite_setup(struct usb_gadget gadget, const struct usb_ctrlrequest ctrl)
composite_setup实现通用的控制命令,function可以扩展实现更多的控制命令
host端数据传输 -> device端
udc_irq —> isr_tr_complete_low —> mReq->req.complete
usb的数据通讯基于endpoint,每个endpoint都一个地址,双向通过这个地址通讯
传输数据之前,需要申请usb_request
1 | struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len) |
发送数据(in端点)
填充req的数据,举例:smd_read(pi->ch, req->buf, avail);
调用usb_ep_queue发送
1 | static inline int usb_ep_queue(struct usb_ep *ep, |
发送完成:执行req->complete
接收数据(out端点)
执行req->complete